/******************************************************************************************
版权所有 2021-2021, 长春理工大学C&I实验室。保留所有权利。
文件名：mpp40_venc.c
作者：Wind    版本：V1.0    日期：2021.1.8
文件描述：
    对Hi3516EV200视频编码过程调用MPP操作的封装。
其它说明：

历史修改记录：
1. 2021-1-8:V1.0 Wind
创建。

2. 2021-6-19:Wind
在7Cv1工程的基础上删除了有关JPEG编码部分。
******************************************************************************************/
//+------------------------------------------------------------------------------------------+
//|                                          文件说明
//+------------------------------------------------------------------------------------------+
//|      VENC（Video Encode，视频编码）即视频编码模块。海思MPP的视频编码支持多路多协议独立编码，
//|  同时支持对编码的图像进行内容叠加或遮挡。本文件中仅为上层封装了编码部分的接口。
//|      为便于实验室的学习和修改，本文件中将添加额外的注释信息。
//|
//|                          ——————————————对VENC的说明——————————————
//|      和VPSS不同，VENC只有通道的概念。VENC模块由编码通道子模块（VENC）和编码协议子模块
//|  （H.264/H.265/JPEG/MJPEG/PRORES）组成。每个VENC通道又包括码率控制器和编码器。
//|      在设置VENC通道时分为以下几个部分：
//|  1. 对VENC通道属性的设置
//|  2. 对RC码率控制器属性的设置
//|  其中对GOP的设置属于1。
//|      在调用VENC通道时编码图像不能超过编码通道尺寸（《参考》P795），所以此版本程序中，删去
//|  例程中对大分辨率的配置语句，仅保留1080P及以下的枚举类型以缩减函数的体积。
//|      Hi3516CV500/Hi3516DV300/Hi3559V200/Hi3556V200/Hi3516EV200/Hi3516EV300/Hi3518EV300
//|  支持的最大VENC通道数为16。——《参考》P795~P796、P953
//+------------------------------------------------------------------------------------------+
//|  编写参考《HiMPP V4.0媒体处理软件开发参考(2019-3-5)》（简称《参考》）。    
//+------------------------------------------------------------------------------------------+
//|                                         头文件包含
//+------------------------------------------------------------------------------------------+
/*|*/#include <stdio.h>      //提供printf等基本输入输出
/*|*/#include <string.h>     //提供字符串操作函数
/*|*/#include <sys/time.h>   //提供时间结构体
/*|*/#include <unistd.h>     //使用Unix系统服务，例如select
/*|*/#include "hi_common.h"  //该文件中定义了海思的数据类型
/*|*/#include "hi_comm_rc.h" //该文件中定义了RC帧率控制数据类型
/*|*/#include "hi_math.h"    //使用了ALIGN_UP宏
/*|*/#include "mpi_sys.h"    //该文件提供了MPI-SYS函数声明
/*|*/#include "mpi_ae.h"     //该文件提供了MPI-ISP函数声明
/*|*/#include "mpi_venc.h"   //该文件提供了MPI-VENC函数声明
//|
/*|*/#include "mpp40_debug.h"
/*|*/#include "mpp40_venc.h"
//+------------------------------------------------------------------------------------------+
//|                                         参数宏定义
//+------------------------------------------------------------------------------------------+
//|  ----------------------------------------
//|  在定义了编码流保存宏时，保存的文件名设置
//|  ----------------------------------------
/*|*/#ifdef SAVE_STREAM_TO_FILE
/*|*/  #define VENC_CHN0_FILENAME  "video_chn0"
/*|*/  #define VENC_CHN1_FILENAME  "video_chn1"
/*|*/  #define VENC_CHN2_FILENAME  "video_chn2"
/*|*/  #define VENC_CHN3_FILENAME  "video_chn3"
/*|*/  #define VENC_CHN4_FILENAME  "video_chn4"
/*|*/  #define VENC_CHN5_FILENAME  "video_chn5"
/*|*/  #define VENC_CHN6_FILENAME  "video_chn6"
/*|*/  #define VENC_CHN7_FILENAME  "video_chn7"
/*|*/  #define VENC_CHN8_FILENAME  "video_chn8"
/*|*/  #define VENC_CHN9_FILENAME  "video_chn9"
/*|*/  #define VENC_CHN10_FILENAME "video_chn10"
/*|*/  #define VENC_CHN11_FILENAME "video_chn11"
/*|*/  #define VENC_CHN12_FILENAME "video_chn12"
/*|*/  #define VENC_CHN13_FILENAME "video_chn13"
/*|*/  #define VENC_CHN14_FILENAME "video_chn14"
/*|*/  #define VENC_CHN15_FILENAME "video_chn15"
/*|*/#endif
//+------------------------------------------------------------------------------------------+
//|                                          全局变量
//+------------------------------------------------------------------------------------------+
//|  ----------------------------------------
//|  通道被占用的状态
//|  0：未使用 1：用于视频流编码 
//|  ----------------------------------------
/*|*/static int EnabledVencChn[VENC_MAX_CHN_NUM] = {0};
//|  ----------------------------------------
//|  视频编码/图像抓拍函数默认设置结构体
//|  当函数传入NULL时，调用此结构体进行缺省配
//|  置，用作学习示例。
//|  [说明]正常情况下，当传入的参数不合法时，
//|  函数应返回执行失败，而不能寄期望于默认设
//|  置。如果函数动作中包含默认设置，则使用到
//|  该默认设置的情况属于合法范围。
//|  ----------------------------------------
/*|*/static const VENC_PARA_S VENCpara_default=
/*|*/{
/*|*/    .VencChn      = 0,
/*|*/    .enPayLoad    = PT_H264,
/*|*/    .enSize       = PIC_1080P,
/*|*/    .enGopMode    = VENC_GOPMODE_NORMALP,
/*|*/    .u32Gop       = 30,
/*|*/    .enRcMode     = RC_CBR,
/*|*/    .u32FrameRate = 30,
/*|*/    .u32Profile   = 0,
/*|*/};
//+------------------------------------------------------------------------------------------+
//|  函数名称：vi_bind_venc
//|  功能描述：绑定VENC编码通道到VI
//|  参数说明：VENC通道号
//|  返回值说明：成功返回0
//|  备注：此处只绑定到VI(PIPE0-Chn0)。
//+------------------------------------------------------------------------------------------+
static int vi_bind_venc(VENC_CHN VencChn)
{
    HI_S32 s32Ret = HI_SUCCESS;
    MPP_CHN_S stSrcChn, stDestChn;
    
    stSrcChn.enModId  = HI_ID_VI;
    stSrcChn.s32DevId = 0;
    stSrcChn.s32ChnId = 0;
    
    stDestChn.enModId  = HI_ID_VENC;
    stDestChn.s32DevId = 0;
    stDestChn.s32ChnId = VencChn;
    
    s32Ret = HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);
    if(s32Ret != HI_SUCCESS)
    {
        MPP_OUTPUT("[MPP40-VENC-ERROR]VI bind VENC failed!\n");
        venc_deinit(VencChn);
        return -1;
    }
    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：vi_unbind_venc
//|  功能描述：进行VI-VENC通道解绑定
//|  参数说明：VENC通道号
//|  返回值说明：无
//|  备注：此处只解绑到VI(PIPE0-Chn0)。
//+------------------------------------------------------------------------------------------+
static void vi_unbind_venc(VENC_CHN VencChn)
{
    MPP_CHN_S stSrcChn, stDestChn;
    
    stSrcChn.enModId  = HI_ID_VI;
    stSrcChn.s32DevId = 0;
    stSrcChn.s32ChnId = 0;
    
    stDestChn.enModId  = HI_ID_VENC;
    stDestChn.s32DevId = 0;
    stDestChn.s32ChnId = VencChn;
    
    HI_MPI_SYS_UnBind(&stSrcChn, &stDestChn);
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：venc_create_chn_and_start
//|  功能描述：创建VENC编码通道并启动
//|  参数说明：VENC参数结构体
//|  返回值说明：成功返回0
//|  备注：只支持建立H264和H265编码的码流。
//|      本函数建立并初始化通道之后，MPP视频编码初始化全部流程结束，可立即开始查询编码码流。
//|    函数使用：
//|        传入NULL则调用默认配置进行编码（H265-1080P）。建立VENC_PARA_S类型的结构体进行初始化
//|      后，传入结构体指针即可初始化相应通道的编码。
//|    一个可能的问题：
//|        当本函数执行完成后，VENC已初始化的通道即开始编码，而当按序初始化多个通道时，先初始
//|      化完成的通道的码流会产生堆积。此时多通道初始化完成后，后续码流获取函数将在启动初期迎
//|      来一个码流处理高峰。
//|    一个客观存在的注意项：
//|        同时能够启动的VENC通道数取决于当前内存多少以及MMZ分配方式等。在Hi3516EV200/Hi3516DV300
//|      上测试过双通道编码和保存成功，证明逻辑的正确性。
//+------------------------------------------------------------------------------------------+
int venc_create_chn_and_start(const VENC_PARA_S *pVENCpara)
{
    HI_S32 s32Ret = HI_SUCCESS;
    
    if(pVENCpara == NULL)
    {
        pVENCpara = &VENCpara_default;
    }
    
    if(EnabledVencChn[pVENCpara->VencChn])
    {
        MPP_OUTPUT("[MPP40-VENC-ERROR]The Channel choosed for ENCODE has already been inited!\n");
        return -1;
    }
    
    //----------------------------------------
    //——————————————关于GOP结构——————————————
    //参考手册P802，Hi3516CV500/Hi3516DV300/
    //Hi3516EV200/Hi3516EV300/Hi3518EV300在H264
    //和H265编码时仅支持NORMALP、DUALP、SMARTP。
    //但是在官方例程中，加入了额外的判断，
    //似乎H264编码协议BP(Base Profile)不支持
    //BIPREDB，但是H264-MP(Main Profile)支持。
    //
    //另外，例程中在码率控制为H264-QPMAP和H265-QPMAP
    //时，将ADVSMARTP的GOP模式重设置为SMARTP。
    //
    //综上，此版本程序仅支持手册中提到的3516EV200
    //支持的三种GOP模式，其它模式的设置不在考虑范围。
    //----------------------------------------
    VENC_GOP_ATTR_S stGopAttr;
    switch(pVENCpara->enGopMode)
    {
        case VENC_GOPMODE_NORMALP:
            stGopAttr.enGopMode              = VENC_GOPMODE_NORMALP;
            stGopAttr.stNormalP.s32IPQpDelta = 2;
        break;
        case VENC_GOPMODE_SMARTP:
            stGopAttr.enGopMode              = VENC_GOPMODE_SMARTP;
            stGopAttr.stSmartP.s32BgQpDelta  = 4;
            stGopAttr.stSmartP.s32ViQpDelta  = 2;
            stGopAttr.stSmartP.u32BgInterval = 90;
        break;
        case VENC_GOPMODE_DUALP:
            stGopAttr.enGopMode             = VENC_GOPMODE_DUALP;
            stGopAttr.stDualP.s32IPQpDelta  = 4;
            stGopAttr.stDualP.s32SPQpDelta  = 2;
            stGopAttr.stDualP.u32SPInterval = 3;
        break;
        default:
            MPP_OUTPUT("[MPP40-VENC-WARNING]Unsupported GOPMODE for Hi3516EV200! Changed to NORMALP!\n");
            stGopAttr.enGopMode              = VENC_GOPMODE_NORMALP;
            stGopAttr.stNormalP.s32IPQpDelta = 2;
        break;
    }
    
    HI_U32 u32SrcFrameRate  = 30;//[说明]源帧率和sensor适配，为30fps
    HI_U32 u32FrameRate     = pVENCpara->u32FrameRate;
    HI_U32 u32StatTime      = 1;
    HI_BOOL bRcnRefShareBuf = HI_TRUE;
    
    //----------------------------------------
    //GOP分组即I帧和I帧之间的间隔，表现在码流上
    //为多包模式下，SPS、PPS、SEI包的间隔数。
    //单包或多包模式见《参考》P811。
    //----------------------------------------
    HI_U32 u32Gop = pVENCpara->u32Gop;
    
    //----------------------------------------
    //编码尺寸的支持需要在此处和PIC_SIZE_E中
    //自行添加。如果考虑到资源，在RC控制选择处
    //也要添加对应处理，以防止按照最大比特率编码。
    //----------------------------------------
    SIZE_S stPicSize;
    switch(pVENCpara->enSize)
    {
        case PIC_VGA:
            stPicSize.u32Width  = 640;
            stPicSize.u32Height = 480;
        break;
        case PIC_720P:
            stPicSize.u32Width  = 1280;
            stPicSize.u32Height = 720;
        break;
        case PIC_1080P:
            stPicSize.u32Width  = 1920;
            stPicSize.u32Height = 1080;
        break;
        default:
            stPicSize.u32Width  = 1920;
            stPicSize.u32Height = 1080;
        break;
    }
    VENC_CHN_ATTR_S stVencChnAttr;
    memcpy(&stVencChnAttr.stGopAttr, &stGopAttr, sizeof(VENC_GOP_ATTR_S));
    stVencChnAttr.stVencAttr.enType = pVENCpara->enPayLoad;
    //----------------------------------------
    //[说明]参照例程，最大编码尺寸与目的尺寸相
    //同。最大尺寸的设置适用动态设置编码分辨率，
    //本程序中在创建通道之前已经指定分辨率，
    //修改仅采用静态方式。
    //----------------------------------------
    stVencChnAttr.stVencAttr.u32MaxPicWidth  = stPicSize.u32Width;
    stVencChnAttr.stVencAttr.u32MaxPicHeight = stPicSize.u32Height;
    stVencChnAttr.stVencAttr.u32PicWidth     = stPicSize.u32Width;
    stVencChnAttr.stVencAttr.u32PicHeight    = stPicSize.u32Height;
    
    //----------------------------------------
    //  ————————————关于Profile————————————
    //  编码的等级
    //  H.264取值范围：[0,3]
    //  0：Baseline
    //  1：Main Profile
    //  2：High Profile
    //  3：svc-t
    //  H.265取值范围：[0,1]
    //  0：Main Profile
    //  1：Main 10 Profile ——> 3516EV200不支持
    //  
    //  ——《参考》P982~P983、P794~P795
    //----------------------------------------
    stVencChnAttr.stVencAttr.u32Profile = pVENCpara->u32Profile;
    stVencChnAttr.stVencAttr.u32BufSize = stPicSize.u32Width*stPicSize.u32Height*1.5;
    stVencChnAttr.stVencAttr.bByFrame   = HI_TRUE;//码流按帧获取还是按包获取
    
    //----------------------------------------
    //关于SMARTP下对u32StatTime的设置，参考例程
    //----------------------------------------
    if(VENC_GOPMODE_SMARTP == pVENCpara->enGopMode)
    {
        u32StatTime = stGopAttr.stSmartP.u32BgInterval/u32Gop;
    }
    else
    {
        u32StatTime = 1;
    }
    
    //----------------------------------------
    //此处的switch是根据编码方式和RC帧率控制的不
    //同填充stVencChnAttr中关于RC的设置。
    //----------------------------------------
    switch(pVENCpara->enPayLoad)
    {
        case PT_H265:
        {
            if(RC_CBR == pVENCpara->enRcMode)
            {
                VENC_H265_CBR_S stH265Cbr;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H265CBR;
                stH265Cbr.u32Gop                = u32Gop;
                stH265Cbr.u32StatTime           = u32StatTime;     /* stream rate statics time(s) */
                stH265Cbr.u32SrcFrameRate       = u32SrcFrameRate; /* input(vi) frame rate */
                stH265Cbr.fr32DstFrameRate      = u32FrameRate;    /* target frame rate */
                switch(pVENCpara->enSize)
                {
                    case PIC_VGA:
                        stH265Cbr.u32BitRate = 1024 + 512*u32SrcFrameRate / 30;
                    break;
                    case PIC_720P:
                        stH265Cbr.u32BitRate = 1024*2 + 1024*u32SrcFrameRate / 30;
                    break;
                    case PIC_1080P:
                        stH265Cbr.u32BitRate = 1024*2 + 2048*u32SrcFrameRate / 30;
                    break;
                    default: //其它小分辨率
                        stH265Cbr.u32BitRate = 256;
                    break;
                }
                memcpy(&stVencChnAttr.stRcAttr.stH265Cbr, &stH265Cbr, sizeof(VENC_H265_CBR_S));
            }
            else if(RC_FIXQP == pVENCpara->enRcMode)
            {
                VENC_H265_FIXQP_S stH265FixQp;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H265FIXQP;
                stH265FixQp.u32Gop              = 30;
                stH265FixQp.u32SrcFrameRate     = u32SrcFrameRate;
                stH265FixQp.fr32DstFrameRate    = u32FrameRate;
                stH265FixQp.u32IQp              = 25;
                stH265FixQp.u32PQp              = 30;
                stH265FixQp.u32BQp              = 32;
                memcpy(&stVencChnAttr.stRcAttr.stH265FixQp, &stH265FixQp, sizeof(VENC_H265_FIXQP_S));
            }
            else if(RC_VBR == pVENCpara->enRcMode)
            {
                VENC_H265_VBR_S stH265Vbr;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H265VBR;
                stH265Vbr.u32Gop                = u32Gop;
                stH265Vbr.u32StatTime           = u32StatTime;
                stH265Vbr.u32SrcFrameRate       = u32SrcFrameRate;
                stH265Vbr.fr32DstFrameRate      = u32FrameRate;
                switch(pVENCpara->enSize)
                {
                    case PIC_VGA:
                        stH265Vbr.u32MaxBitRate = 1024 + 512*u32SrcFrameRate / 30;
                    break;
                    case PIC_720P:
                        stH265Vbr.u32MaxBitRate = 1024*2 + 1024*u32SrcFrameRate / 30;
                    break;
                    case PIC_1080P:
                        stH265Vbr.u32MaxBitRate = 1024*2 + 2048*u32SrcFrameRate / 30;
                    break;
                    default :
                        stH265Vbr.u32MaxBitRate = 1024*15 + 2048*u32SrcFrameRate / 30;
                    break;
                }
                memcpy(&stVencChnAttr.stRcAttr.stH265Vbr, &stH265Vbr, sizeof(VENC_H265_VBR_S));
            }
            else if(RC_AVBR == pVENCpara->enRcMode)
            {
                VENC_H265_AVBR_S stH265AVbr;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H265AVBR;
                stH265AVbr.u32Gop               = u32Gop;
                stH265AVbr.u32StatTime          = u32StatTime;
                stH265AVbr.u32SrcFrameRate      = u32SrcFrameRate;
                stH265AVbr.fr32DstFrameRate     = u32FrameRate;
                switch(pVENCpara->enSize)
                {
                    case PIC_VGA:
                        stH265AVbr.u32MaxBitRate = 1024 + 512*u32SrcFrameRate / 30;
                    break;
                    case PIC_720P:
                        stH265AVbr.u32MaxBitRate = 1024*2 + 1024*u32SrcFrameRate / 30;
                    break;
                    case PIC_1080P:
                        stH265AVbr.u32MaxBitRate = 1024*2 + 2048*u32SrcFrameRate / 30;
                    break;
                    default :
                        stH265AVbr.u32MaxBitRate = 1024*15 + 2048*u32SrcFrameRate / 30;
                    break;
                }
                memcpy(&stVencChnAttr.stRcAttr.stH265AVbr, &stH265AVbr, sizeof(VENC_H265_AVBR_S));
            }
            else if(RC_QVBR == pVENCpara->enRcMode)
            {
                VENC_H265_QVBR_S stH265QVbr;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H265QVBR;
                stH265QVbr.u32Gop               = u32Gop;
                stH265QVbr.u32StatTime          = u32StatTime;
                stH265QVbr.u32SrcFrameRate      = u32SrcFrameRate;
                stH265QVbr.fr32DstFrameRate     = u32FrameRate;
                switch(pVENCpara->enSize)
                {
                    case PIC_VGA:
                        stH265QVbr.u32TargetBitRate = 1024 + 512*u32SrcFrameRate / 30;
                        break;
                    case PIC_720P:
                        stH265QVbr.u32TargetBitRate = 1024*2 + 1024*u32SrcFrameRate / 30;
                        break;
                    case PIC_1080P:
                        stH265QVbr.u32TargetBitRate = 1024*2 + 2048*u32SrcFrameRate / 30;
                        break;
                    default :
                        stH265QVbr.u32TargetBitRate = 1024*15 + 2048*u32SrcFrameRate / 30;
                        break;
                }
                memcpy(&stVencChnAttr.stRcAttr.stH265QVbr, &stH265QVbr, sizeof(VENC_H265_QVBR_S));
            }
            else if(RC_CVBR == pVENCpara->enRcMode)
            {
                VENC_H265_CVBR_S stH265CVbr;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H265CVBR;
                stH265CVbr.u32Gop               = u32Gop;
                stH265CVbr.u32StatTime          = u32StatTime;
                stH265CVbr.u32SrcFrameRate      = u32SrcFrameRate;
                stH265CVbr.fr32DstFrameRate     = u32FrameRate;
                stH265CVbr.u32LongTermStatTime  = 1;
                stH265CVbr.u32ShortTermStatTime = u32StatTime;
                switch(pVENCpara->enSize)
                {
                    case PIC_VGA:
                        stH265CVbr.u32MaxBitRate         = 1024 + 512*u32SrcFrameRate / 30;
                        stH265CVbr.u32LongTermMaxBitrate = 1024 + 512*u32SrcFrameRate / 30;
                        stH265CVbr.u32LongTermMinBitrate = 256;
                    break;
                    case PIC_720P:
                        stH265CVbr.u32MaxBitRate         = 1024*3 + 1024*u32SrcFrameRate / 30;
                        stH265CVbr.u32LongTermMaxBitrate = 1024*2 + 1024*u32SrcFrameRate / 30;
                        stH265CVbr.u32LongTermMinBitrate = 512;
                    break;
                    case PIC_1080P:
                        stH265CVbr.u32MaxBitRate         = 1024*2 + 2048*u32SrcFrameRate / 30;
                        stH265CVbr.u32LongTermMaxBitrate = 1024*2 + 2048*u32SrcFrameRate / 30;
                        stH265CVbr.u32LongTermMinBitrate = 1024;
                    break;
                    default :
                        stH265CVbr.u32MaxBitRate         = 1024*24 + 5120*u32SrcFrameRate / 30;
                        stH265CVbr.u32LongTermMaxBitrate = 1024*15 + 2048*u32SrcFrameRate / 30;
                        stH265CVbr.u32LongTermMinBitrate = 1024*5;
                    break;
                }
                memcpy(&stVencChnAttr.stRcAttr.stH265CVbr, &stH265CVbr, sizeof(VENC_H265_CVBR_S));
            }
            else if(RC_QPMAP == pVENCpara->enRcMode)
            {
                VENC_H265_QPMAP_S stH265QpMap;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H265QPMAP;
                stH265QpMap.u32Gop              = u32Gop;
                stH265QpMap.u32StatTime         = u32StatTime;
                stH265QpMap.u32SrcFrameRate     = u32SrcFrameRate;
                stH265QpMap.fr32DstFrameRate    = u32FrameRate;
                stH265QpMap.enQpMapMode         = VENC_RC_QPMAP_MODE_MEANQP;
                memcpy(&stVencChnAttr.stRcAttr.stH265QpMap, &stH265QpMap, sizeof(VENC_H265_QPMAP_S));
            }
            else
            {
                MPP_OUTPUT("[MPP40-VENC-ERROR]%s,%d,enRcMode(%d) not support!\n",__FUNCTION__,__LINE__,pVENCpara->enRcMode);
                return HI_FAILURE;
            }
            stVencChnAttr.stVencAttr.stAttrH265e.bRcnRefShareBuf = bRcnRefShareBuf;
        }
        break;
        case PT_H264:
        {
            if(RC_CBR == pVENCpara->enRcMode)
            {
                VENC_H264_CBR_S stH264Cbr;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
                stH264Cbr.u32Gop                = u32Gop;          /*the interval of IFrame*/
                stH264Cbr.u32StatTime           = u32StatTime;     /* stream rate statics time(s) */
                stH264Cbr.u32SrcFrameRate       = u32SrcFrameRate; /* input(vi) frame rate */
                stH264Cbr.fr32DstFrameRate      = u32FrameRate;    /* target frame rate */
                switch(pVENCpara->enSize)
                {
                    case PIC_VGA:
                        stH264Cbr.u32BitRate = 1024 + 512*u32SrcFrameRate / 30;
                    break;
                    case PIC_720P:
                        stH264Cbr.u32BitRate = 1024*2 + 1024*u32SrcFrameRate / 30;
                    break;
                    case PIC_1080P:
                        stH264Cbr.u32BitRate = 1024*2 + 2048*u32SrcFrameRate / 30;
                    break;
                    default:
                        stH264Cbr.u32BitRate = 512;
                    break;
                }

                memcpy(&stVencChnAttr.stRcAttr.stH264Cbr, &stH264Cbr, sizeof(VENC_H264_CBR_S));
            }
            else if(RC_FIXQP == pVENCpara->enRcMode)
            {
                VENC_H264_FIXQP_S stH264FixQp;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264FIXQP;
                stH264FixQp.u32Gop              = 30;
                stH264FixQp.u32SrcFrameRate     = u32SrcFrameRate;
                stH264FixQp.fr32DstFrameRate    = u32FrameRate;
                stH264FixQp.u32IQp              = 25;
                stH264FixQp.u32PQp              = 30;
                stH264FixQp.u32BQp              = 32;
                memcpy(&stVencChnAttr.stRcAttr.stH264FixQp, &stH264FixQp, sizeof(VENC_H264_FIXQP_S));
            }
            else if(RC_VBR == pVENCpara->enRcMode)
            {
                VENC_H264_VBR_S stH264Vbr;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264VBR;
                stH264Vbr.u32Gop                = u32Gop;
                stH264Vbr.u32StatTime           = u32StatTime;
                stH264Vbr.u32SrcFrameRate       = u32SrcFrameRate;
                stH264Vbr.fr32DstFrameRate      = u32FrameRate;
                switch(pVENCpara->enSize)
                {
                    case PIC_VGA:
                        stH264Vbr.u32MaxBitRate = 1024 + 512*u32SrcFrameRate / 30;
                    break;
                    case PIC_720P:
                        stH264Vbr.u32MaxBitRate = 1024*2 + 1024*u32SrcFrameRate / 30;
                    break;
                    case PIC_1080P:
                        stH264Vbr.u32MaxBitRate = 1024*2 + 2048*u32SrcFrameRate / 30;
                    break;
                    default :
                        stH264Vbr.u32MaxBitRate = 1024*15 + 2048*u32SrcFrameRate / 30;
                    break;
                }
                memcpy(&stVencChnAttr.stRcAttr.stH264Vbr, &stH264Vbr, sizeof(VENC_H264_VBR_S));
            }
            else if(RC_AVBR == pVENCpara->enRcMode)
            {
                VENC_H264_VBR_S stH264AVbr;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264AVBR;
                stH264AVbr.u32Gop               = u32Gop;
                stH264AVbr.u32StatTime          = u32StatTime;
                stH264AVbr.u32SrcFrameRate      = u32SrcFrameRate;
                stH264AVbr.fr32DstFrameRate     = u32FrameRate;
                switch(pVENCpara->enSize)
                {
                    case PIC_VGA:
                        stH264AVbr.u32MaxBitRate = 1024 + 512*u32SrcFrameRate / 30;
                    break;
                    case PIC_720P:
                        stH264AVbr.u32MaxBitRate = 1024*2 + 1024*u32SrcFrameRate / 30;
                    break;
                    case PIC_1080P:
                        stH264AVbr.u32MaxBitRate = 1024*2 + 2048*u32SrcFrameRate / 30;
                    break;
                    default :
                        stH264AVbr.u32MaxBitRate = 1024*15 + 2048*u32SrcFrameRate / 30;
                    break;
                }
                memcpy(&stVencChnAttr.stRcAttr.stH264AVbr, &stH264AVbr, sizeof(VENC_H264_AVBR_S));
            }
            else if(RC_QVBR == pVENCpara->enRcMode)
            {
                VENC_H264_QVBR_S stH264QVbr;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264QVBR;
                stH264QVbr.u32Gop               = u32Gop;
                stH264QVbr.u32StatTime          = u32StatTime;
                stH264QVbr.u32SrcFrameRate      = u32SrcFrameRate;
                stH264QVbr.fr32DstFrameRate     = u32FrameRate;
                switch(pVENCpara->enSize)
                {
                    case PIC_VGA:
                        stH264QVbr.u32TargetBitRate = 1024 + 512*u32SrcFrameRate / 30;
                    break;
                    case PIC_720P:
                        stH264QVbr.u32TargetBitRate = 1024*2 + 1024*u32SrcFrameRate / 30;
                    break;
                    case PIC_1080P:
                        stH264QVbr.u32TargetBitRate = 1024*2 + 2048*u32SrcFrameRate / 30;
                    break;
                    default :
                        stH264QVbr.u32TargetBitRate = 1024*15 + 2048*u32SrcFrameRate / 30;
                    break;
                }
                memcpy(&stVencChnAttr.stRcAttr.stH264QVbr, &stH264QVbr, sizeof(VENC_H264_QVBR_S));
            }
            else if(RC_CVBR == pVENCpara->enRcMode)
            {
                VENC_H264_CVBR_S stH264CVbr;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CVBR;
                stH264CVbr.u32Gop               = u32Gop;
                stH264CVbr.u32StatTime          = u32StatTime;
                stH264CVbr.u32SrcFrameRate      = u32SrcFrameRate;
                stH264CVbr.fr32DstFrameRate     = u32FrameRate;
                stH264CVbr.u32LongTermStatTime  = 1;
                stH264CVbr.u32ShortTermStatTime = u32StatTime;
                switch(pVENCpara->enSize)
                {
                    case PIC_VGA:
                        stH264CVbr.u32MaxBitRate         = 1024 + 512*u32SrcFrameRate / 30;
                        stH264CVbr.u32LongTermMaxBitrate = 1024 + 512*u32SrcFrameRate / 30;
                        stH264CVbr.u32LongTermMinBitrate = 256;
                    break;
                    case PIC_720P:
                        stH264CVbr.u32MaxBitRate         = 1024*3 + 1024*u32SrcFrameRate  / 30;
                        stH264CVbr.u32LongTermMaxBitrate = 1024*2 + 1024*u32SrcFrameRate / 30;
                        stH264CVbr.u32LongTermMinBitrate = 512;
                    break;
                    case PIC_1080P:
                        stH264CVbr.u32MaxBitRate         = 1024*2 + 2048*u32SrcFrameRate / 30;
                        stH264CVbr.u32LongTermMaxBitrate = 1024*2 + 2048*u32SrcFrameRate / 30;
                        stH264CVbr.u32LongTermMinBitrate = 1024;
                    break;
                    default :
                        stH264CVbr.u32MaxBitRate         = 1024*24 + 5120*u32SrcFrameRate / 30;
                        stH264CVbr.u32LongTermMaxBitrate = 1024*15 + 2048*u32SrcFrameRate / 30;
                        stH264CVbr.u32LongTermMinBitrate = 1024*5;
                    break;
                }
                memcpy(&stVencChnAttr.stRcAttr.stH264CVbr, &stH264CVbr, sizeof(VENC_H264_CVBR_S));
            }
            else if(RC_QPMAP == pVENCpara->enRcMode)
            {
                VENC_H264_QPMAP_S stH264QpMap;

                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264QPMAP;
                stH264QpMap.u32Gop              = u32Gop;
                stH264QpMap.u32StatTime         = u32StatTime;
                stH264QpMap.u32SrcFrameRate     = u32SrcFrameRate;
                stH264QpMap.fr32DstFrameRate    = u32FrameRate;
                memcpy(&stVencChnAttr.stRcAttr.stH264QpMap, &stH264QpMap, sizeof(VENC_H264_QPMAP_S));
            }
            else
            {
                MPP_OUTPUT("[MPP40-VENC-ERROR]%s,%d,enRcMode(%d) not support!\n",__FUNCTION__,__LINE__,pVENCpara->enRcMode);
                return HI_FAILURE;
            }
            stVencChnAttr.stVencAttr.stAttrH264e.bRcnRefShareBuf = bRcnRefShareBuf;
        }
        break;
        default:
            MPP_OUTPUT("[MPP40-VENC-ERROR]Unsupport PT in this function! You can see sample for more infomation.\n");
        return HI_ERR_VENC_NOT_SUPPORT;
    }
    
    s32Ret = HI_MPI_VENC_CreateChn(pVENCpara->VencChn, &stVencChnAttr);
    if(HI_SUCCESS != s32Ret)
    {
        MPP_OUTPUT("[MPP40-VENC-ERROR]HI_MPI_VENC_CreateChn[%d] faild with %#x!\n", pVENCpara->VencChn, s32Ret);
        HI_MPI_VENC_DestroyChn(pVENCpara->VencChn);
        return s32Ret;
    }
    
    VENC_RECV_PIC_PARAM_S stRecvParam;
    stRecvParam.s32RecvPicNum = pVENCpara->s32FrameNumber;
    s32Ret = HI_MPI_VENC_StartRecvFrame(pVENCpara->VencChn, &stRecvParam);
    if(HI_SUCCESS != s32Ret)
    {
        MPP_OUTPUT("[MPP40-VENC-ERROR]HI_MPI_VENC_StartRecvPic faild with %#x!\n", s32Ret);
        venc_deinit(pVENCpara->VencChn);
        return s32Ret;
    }
    
    s32Ret = vi_bind_venc(pVENCpara->VencChn);
    if(0!=s32Ret)
    {
        venc_deinit(pVENCpara->VencChn);
        return -1;
    }
    
    //----------------------------------------
    //将已经初始化的VENC通道记录下来
    //----------------------------------------
    EnabledVencChn[pVENCpara->VencChn]=1;
    
    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：venc_deinit
//|  功能描述：进行VENC通道去初始化
//|  参数说明：VENC通道号
//|  返回值说明：无
//|  备注：此处只解绑到VI(PIPE0-Chn0)。
//+------------------------------------------------------------------------------------------+
void venc_deinit(VENC_CHN VencChn)
{
    vi_unbind_venc(VencChn);
    HI_MPI_VENC_StopRecvFrame(VencChn);
    HI_MPI_VENC_DestroyChn(VencChn);
    //----------------------------------------
    //将已经去初始化的VENC通道记录下来
    //----------------------------------------
    EnabledVencChn[VencChn] = 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：venc_getstream
//|  功能描述：从VENC通道中读取码流并处理
//|  参数说明：int型循环结束标志，为0时结束循环码流；处理函数的函数名
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
void venc_getstream(bool *get_stream_enable, void (*pstreamfuction)(int i, VENC_STREAM_S *pstStream))
{
    HI_S32 i, s32Ret;
    fd_set venc_readfds;
    HI_S32 VencFd[VENC_MAX_CHN_NUM];
    for(i = 0; i < VENC_MAX_CHN_NUM; i++)
    {
        VencFd[i] = -1;
    }
    VENC_STREAM_S stStream;   //码流结构体
    VENC_CHN_STATUS_S stStat; //VENC通道状态结构体
    struct timeval timeoutval;//select轮询超时时间
#ifdef SAVE_STREAM_TO_FILE
    HI_CHAR FileName[VENC_MAX_CHN_NUM][64];
    {
        //----------------------------------------
        //[说明]给每个通道保存下来的视频裸流文件起
        //个名字为了方便后续的更改和特定需求。
        //
        //此版本不使用循环为通道赋值固定的文件前缀。
        //此处文件名长度不要超过60Bytes，后续将根
        //据通道荷载添加后缀名。
        //
        //此处的文件名只有使用到对应通道进行视频编码
        //时才有效。
        //----------------------------------------
        strcpy(&FileName[0][0],  VENC_CHN0_FILENAME);
        strcpy(&FileName[1][0],  VENC_CHN1_FILENAME);
        strcpy(&FileName[2][0],  VENC_CHN2_FILENAME);
        strcpy(&FileName[3][0],  VENC_CHN3_FILENAME);
        strcpy(&FileName[4][0],  VENC_CHN4_FILENAME);
        strcpy(&FileName[5][0],  VENC_CHN5_FILENAME);
        strcpy(&FileName[6][0],  VENC_CHN6_FILENAME);
        strcpy(&FileName[7][0],  VENC_CHN7_FILENAME);
        strcpy(&FileName[8][0],  VENC_CHN8_FILENAME);
        strcpy(&FileName[9][0],  VENC_CHN9_FILENAME);
        strcpy(&FileName[10][0], VENC_CHN10_FILENAME);
        strcpy(&FileName[11][0], VENC_CHN11_FILENAME);
        strcpy(&FileName[12][0], VENC_CHN12_FILENAME);
        strcpy(&FileName[13][0], VENC_CHN13_FILENAME);
        strcpy(&FileName[14][0], VENC_CHN14_FILENAME);
        strcpy(&FileName[15][0], VENC_CHN15_FILENAME);
    }
    FILE* pFile[VENC_MAX_CHN_NUM] = {0};//每个通道保存时的文件句柄
    //----------------------------------------
    //[说明]查询每个已开启的通道的荷载类型，在
    //对应文件名字符串后添加后缀。
    //----------------------------------------
    PAYLOAD_TYPE_E enPayLoadType[VENC_MAX_CHN_NUM];//获取的每个通道荷载类型
    VENC_CHN_ATTR_S stVencChnAttr;//获取通道荷载类型时的属性接收变量
#endif
    HI_S32 maxfd = 0;
    for(i = 0; i < VENC_MAX_CHN_NUM; i++)
    {
        if(EnabledVencChn[i])//查看通道是否在开启状态
        {
#ifdef SAVE_STREAM_TO_FILE
            s32Ret = HI_MPI_VENC_GetChnAttr(i, &stVencChnAttr);
            if(s32Ret != HI_SUCCESS)
            {
                MPP_OUTPUT("[MPP40-VENC-ERROR]HI_MPI_VENC_GetChnAttr Chn[%d] failed with %#x!\n",i,s32Ret);
                return;
            }
            enPayLoadType[i] = stVencChnAttr.stVencAttr.enType;
            //----------------------------------------
            //对图像流的保存在接收时打开并写入文件，
            //此处增加对检测到JPEG荷载时的略过处理.
            //对视频流荷载打开对应的视频文件。
            //----------------------------------------
            if(EnabledVencChn[i] == 1)
            {
                switch(enPayLoadType[i])
                {
                    case PT_H264:
                        strcat(&FileName[i][0], ".h264");
                    break;
                    case PT_H265:
                        strcat(&FileName[i][0], ".h265");
                    break;
                    default:
                        MPP_OUTPUT("[MPP40-VENC-ERROR]Get PayLoadType[%d] failed!",i);
                        return;
                    break;
                }
                
                pFile[i] = fopen(&FileName[i][0], "wb");
                if(!pFile[i])
                {
                    MPP_OUTPUT("[MPP40-VENC-ERROR]Open file[%s] failed!\n", &FileName[i][0]);
                    return;
                }
            }
#endif
            //----------------------------------------
            //[说明]将通道的VENC设备文件句柄存放起来，
            //以便后续查询。此处存放的句柄包括荷载为图
            //像流的通道。
            //----------------------------------------
            VencFd[i] = HI_MPI_VENC_GetFd(i);
            if(VencFd[i] < 0)
            {
                MPP_OUTPUT("[MPP40-VENC-ERROR]HI_MPI_VENC_GetFd failed with %#x!\n", VencFd[i]);
                return;
            }
            
            //----------------------------------------
            //[说明]获取句柄中最大者，便于后续select函数
            //----------------------------------------
            if(maxfd <= VencFd[i])
            {
                maxfd = VencFd[i];
            }
        }
    }
    while(*get_stream_enable)
    {
        FD_ZERO(&venc_readfds);
        //----------------------------------------
        //[说明]select函数成功返回时会将未准备好的
        //描述符位清零，所以每次轮询都需要重新设置
        //venc_readfds。
        //----------------------------------------
        for(i = 0; i < VENC_MAX_CHN_NUM; i++)
        {
            if(VencFd[i] >= 0)
            {
                FD_SET(VencFd[i], &venc_readfds);//将Venc句柄加入到venc_readfds
            }
        }
        timeoutval.tv_sec  = 2;
        timeoutval.tv_usec = 0;//设置超时时间
        //----------------------------------------
        //[说明]select函数返回值小于0说明发生错误，
        //等于0说明在设置的时间范围内，监视的描述符
        //没有发生动作。大于0说明监视的描述符有可读
        //或可写的状态改变（取决于设置）。
        //----------------------------------------
        s32Ret = select(maxfd+1, &venc_readfds, NULL, NULL, &timeoutval);//检查可读性
        if(s32Ret < 0)
        {
            MPP_OUTPUT("[MPP40-VENC-ERROR]Select error!\n");
            break;
        }
        else if(s32Ret == 0)
        {
            MPP_OUTPUT("[MPP40-VENC-INFO]Get VENC stream timeout.\n");
            continue;
        }
        else
        {
            for(i = 0; i < VENC_MAX_CHN_NUM; i++)//在所有通道范围内轮询-->
            {
                if(EnabledVencChn[i])//其中已开启编码的通道-->
                {
                    if(FD_ISSET(VencFd[i], &venc_readfds))//发生了读状态改变，则：
                    {
                        memset(&stStream, 0, sizeof(stStream));//清空码流结构体
                        //----------------------------------------
                        //查询对应通道的状态，获取码流包数量等信息
                        //----------------------------------------
                        s32Ret = HI_MPI_VENC_QueryStatus(i, &stStat);
                        if(HI_SUCCESS != s32Ret)
                        {
                            MPP_OUTPUT("[MPP40-VENC-ERROR]HI_MPI_VENC_QueryStatus Chn[%d] failed with %#x!\n", i, s32Ret);
                            break;
                        }
                        //----------------------------------------
                        //[说明]因为监视到发生了读状态改变，所以使
                        //用select方法后这里的条件不应该发生。
                        //在不使用select、单独一个通道进行轮询的时候，
                        //可以通过此语句进行条件判断。
                        //----------------------------------------
                        if(0 == stStat.u32CurPacks)
                        {
                            MPP_OUTPUT("[MPP40-VENC-WARNING]NOTE: Current frame is NULL!\n");
                            continue;
                        }
                        //----------------------------------------
                        //根据码流包的数量申请内存用于存放码流
                        //----------------------------------------
                        stStream.pstPack = (VENC_PACK_S*)malloc(sizeof(VENC_PACK_S)*stStat.u32CurPacks);
                        if(NULL == stStream.pstPack)
                        {
                            MPP_OUTPUT("malloc stream pack failed!\n");
                            break;
                        }
                        //----------------------------------------
                        //[续]而后就可以拿着这个结构体去领取码流
                        //----------------------------------------
                        stStream.u32PackCount = stStat.u32CurPacks;
                        s32Ret=HI_MPI_VENC_GetStream(i, &stStream,HI_TRUE);
                        if(HI_SUCCESS != s32Ret)
                        {
                            free(stStream.pstPack);
                            stStream.pstPack = NULL;
                            MPP_OUTPUT("[MPP40-VENC-ERROR]HI_MPI_VENC_GetStream failed with %#x!\n", s32Ret);
                            break;
                        }
                        //----------------------------------------
                        //  ————————————码流处理说明————————————
                        //对码流获取完成之后，此处进行处理。
                        //
                        //在多包模式下，H264的I帧分为SPS、PPS、SEI、
                        //Islice四个NAL包。H265的I帧分为VPS、SPS、
                        //PPS、SEI、Islice五个NAL包。
                        //
                        //当仅图像编码时，由于抓拍不是实时的，所以
                        //出现Get VENC stream timeout.的提示是正常
                        //的，因为select循环必然超时。
                        //
                        //[注意]需要注意的是，在这个查询的循环里，
                        //变量i不仅用于循环，还代表了当前处理的通道号。
                        //因此在本段进行码流处理时应避免使用i变量作为
                        //循环变量或临时变量，以免发生错误或误操作。
                        //----------------------------------------
                        {
                            pstreamfuction(i, &stStream);//将当前通道的码流结构体地址传给处理函数
#ifdef SAVE_STREAM_TO_FILE
                            if(EnabledVencChn[i] == 1)//当此包为视频流时
                            {
                                HI_S32 packagelength;
                                HI_U8 *stream;
                                HI_S32 j;
                                for(j = 0; j < stStream.u32PackCount; j++)
                                {
                                    stream = stStream.pstPack[j].pu8Addr + stStream.pstPack[j].u32Offset;
                                    packagelength = stStream.pstPack[j].u32Len - stStream.pstPack[j].u32Offset;//发送长度是帧头当前的长度
                                    
                                    fwrite(stream, packagelength, 1, pFile[i]);
                                    fflush(pFile[i]);
                                    
                                    MPP_OUTPUT("PackageLength[%d]=%d ", j, packagelength);
                                }
                                MPP_OUTPUT("\n");
                            }
#endif
                        }
                        
                        //----------------------------------------
                        //码流处理完成后应及时释放，以免造成编码暂停
                        //----------------------------------------
                        HI_MPI_VENC_ReleaseStream(i, &stStream);
                        free(stStream.pstPack);
                        stStream.pstPack = NULL;
                    }
                }
            }
        }
    }
#ifdef SAVE_STREAM_TO_FILE
    for(i = 0; i < VENC_MAX_CHN_NUM; i++)
    {
        if(EnabledVencChn[i])
        {
            if(pFile[i])
            {
                fclose(pFile[i]);
            }
        }
    }
#endif
}
